#include "subscriptionlistmodel.h"
#include "completers.h"
#include "mainwindow.h"
#include <QtGui>
#include <QMessageBox>
#include <QFileDialog>
#include "urlutils.h"
#include <QNetworkReply>
#include <QNetworkAccessManager>


// ------------------------- List of Items --------------------------

ItemsListModel::ItemsListModel(QObject *parent)
    : QAbstractListModel(parent), m_subscription(0) {
    m_inDescription = false;
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[UrlRole] = "url";
    roles[PathRole] = "path";
    roles[ProgressRole] = "progress";
    roles[PubDateRole] = "pubDate";
    roles[PictureRole] = "pictureUrl";
    roles[StatusRole] = "status";
    roles[IsAvailableRole] = "isAvailable";
    roles[DescriptionRole] = "description";
    setRoleNames(roles);
    /*
    m_defaultSubscription = Subscription("Subscriptions", QUrl("subscriptions:/"));
    m_subscription = &m_defaultSubscription;
    Episode e(m_subscription);
    e.name = "Subscriptions";
    e.downloadLink = QUrl("subscriptions:/");
    m_items << e;
    */
    connect(&m_manager, SIGNAL(finished(QNetworkReply*)),
            SLOT(downloadXmlFinished(QNetworkReply*)), Qt::UniqueConnection);
    connect(&m_downloadManager, SIGNAL(updateViews(Episode)),
            this, SLOT(episodeChanged(Episode)));

}


void ItemsListModel::downloadEpisode(int i) {
    Episode& e = m_items[i];
    Download* d = m_downloadManager.getDownload(e);
    if (d == 0) {
        m_downloadManager.start(e);
        return;
    }
    switch (d->status) {
        case Download::Downloading:
            m_downloadManager.abort(e);
            break;
        default:
            m_downloadManager.start(e);
            break;
    }
}

void ItemsListModel::episodeChanged(Episode e) {
    for (int i=0; i<m_items.length(); ++i)  {
        if (m_items[i] == e) {
            emit dataChanged(index(i), index(i));
//            qDebug() << QString("DataChangedRow:(%1)").arg(i);
            break;
        }
    }
}

int ItemsListModel::rowCount(const QModelIndex &parent) const {
        if (parent == QModelIndex()) return m_items.count();
        else return 0;
}

void ItemsListModel::deleteEpisode(int idx) {
    Episode& e = m_items[idx];
    m_downloadManager.remove(e);
    QString fileName = e.downloadPath();
    int response = QMessageBox::question(MainWindow::instance(),
        tr("Delete File"), QString ("delete ") + fileName, QMessageBox::Yes, QMessageBox::No);
    if (response == QMessageBox::Yes) {
        QFile f(fileName);
        f.remove();
    }
    emit dataChanged(index(idx, 0), index(idx, 0));
    emit subscriptionUpdated(m_subscription);
    reset();
}

QVariant ItemsListModel::data(const QModelIndex &index, int role) const {
    int r = index.row();
    if (r >= m_items.count()) return QVariant();
    if (r < 0) return QVariant();
    const Episode& e = m_items[r];
    Download *d = m_downloadManager.getDownload(e);

    if ((role == Qt::DisplayRole) || (role == NameRole)) {
        return e.name;
    }
    if ((role == Qt::ToolTipRole) || (role == DescriptionRole)) {
        return "<p>" + e.description + "</p>";
    }
    if (role == ProgressRole) {
        if (d != 0) return d->progress;
        if (e.isAvailable()) return 100;
        else return 0;
    }
    if (role == IsAvailableRole) {
        return e.isAvailable();
    }
    if ((role == Qt::EditRole) || (role == UrlRole)) {
        return e.downloadLink;
    }
    if (role == StatusRole) {
        if (d == 0) {
            if (e.isAvailable()) return Download::Complete;
            else return Download::NotStarted;
        }
        return d->status;
    }

    if (role == PictureRole) {
        if (d == 0) {
            if (m_items[r].isAvailable()) return "qrc:/icons/player_play.svgz";
            else return "qrc:/icons/download.svgz";
        }
        if (d->status == Download::NotStarted) return "qrc/icons/download.svgz";
        if (d->status == Download::Aborted) return "qrc:/icons/reload.png";
        if (d->status == Download::Downloading) return "qrc:/icons/cancel.png";
        if (d->status == Download::Complete) return "qrc:/icons/player_play.svgz";
    }
    if (role == PathRole) {
        return m_items[r].downloadPath();
    }
    if (role == PubDateRole) {
        return ago(m_items[r].pubDate);
    }
    return QVariant();

}



void ItemsListModel::refreshIfNecessary(Subscription* s) {
    if (m_subscription == s) {
        setSubscripton(s);
    }
}

void ItemsListModel::setSubscripton(Subscription *s) {
    m_subscription = s;
    QString filename = s->downloadPath("episodes.xml");
    QFileInfo fi(filename);
    if (fi.exists()) {
        parseXml(filename);
        return;
    }


    // Load XML file from URL - fetch list of items
    QNetworkRequest request(s->url);
    m_manager.get(request);
}



void ItemsListModel::downloadXmlFinished(QNetworkReply *reply) {
    QUrl url = reply->url();
    if (reply->error()) {
        qDebug() << QString("Download of %1 failed: %2\n")
                .arg(url.toEncoded().constData())
                .arg(qPrintable(reply->errorString()));
        return;
    }
    reply->deleteLater();
    // save XML to disk:
    QString filename = m_subscription->downloadPath("episodes.xml");


    QFile f(filename);
    if (f.open(QIODevice::WriteOnly)) {
        f.write(reply->readAll());
    }
    else {
        qDebug() << "Unable to save file: " << filename;
        return;
    }
    f.close();

    // m_currentDownloads.removeAll(reply);
    parseXml(filename);
}


void ItemsListModel::parseXml(QString filename) {
    QFile f(filename);
    // parse XML from file.
    if (!f.open(QIODevice::ReadOnly)) {
        qDebug() << "Unable to open file: " << filename;
    }
    m_streamReader.setDevice(&f);
    bool firstItem = false;
    emit beginResetModel();
    // remove items that are not downloaded
    for (int i=m_items.count() -1; i>=0; --i) {
        // keep downloaded items that are in another subscription
        if ((m_downloadManager.getDownload(m_items[i]) != 0) &&
           (m_items[i].subscription != m_subscription)) continue;
        m_items.removeAt(i);
    }

    QDateTime uniqueTime;
    m_subscription->badDates= false;
    m_currentReading = Episode(m_subscription);

    while (!m_streamReader.atEnd()) {
        if (m_items.count() >= 20) break;
        if (!m_streamReader.readNext()) break;
        QString name = m_streamReader.name().toString();
        if (!firstItem && name != "item") continue;
/*        if (m_streamReader.isEndElement() && (name == "description")) {
                m_inDescription=false;
                continue;
        }
        if (m_inDescription) {
            m_currentReading.description += m_streamReader.readElementText();
            continue;
        } */
        if (name == "item") {
            if (firstItem) {
                if (m_currentReading.isComplete()) {
                    m_currentReading.isAvailable();
                    beginInsertRows(QModelIndex(), m_items.count(), m_items.count());
                    m_items << m_currentReading;
                    endInsertRows();
                }
                else {
                    if (!m_currentReading.name.isEmpty())
                    qDebug() << "Incomplete podcast item: " << m_currentReading.name << m_currentReading.pubDate.toString() << m_currentReading.downloadLink  << m_currentReading.description << endl;
                }
            }
            firstItem =true;
            m_currentReading = Episode(m_subscription);
        }
        else if (name == "title") {
            m_currentReading.name = m_streamReader.readElementText();
//            qDebug() << "Title: " << m_currentReading.name;
        }
        else if (name == "description") {
//            m_inDescription = true;
            m_currentReading.description = m_streamReader.readElementText(QXmlStreamReader::IncludeChildElements);
//            qDebug() << "Description: " << m_currentReading.description;
        }
        else if (name == "pubDate") {
            QString dateStr = m_streamReader.readElementText();
            // Try 3 different date formats. More to be added when I encounter them.
            m_currentReading.pubDate = QDateTime::fromString(
                    dateStr.mid(5, 17), "dd MMM yyyy HH:mm");
            if (m_currentReading.pubDate.isNull())
                   m_currentReading.pubDate = QDateTime::fromString(
                    dateStr.mid(4,20), "dd MMM yyyy HH:mm:ss");
            if (m_currentReading.pubDate.isNull()) {
                m_currentReading.pubDate = QDateTime::fromString(
                    dateStr.mid(5, 18), "dd MMM yy HH:mm:ss").addYears(100);
            }
            if (m_currentReading.pubDate.isNull()) {
                m_currentReading.pubDate = QDateTime::fromString(
                    dateStr.left(dateStr.length() - 4), "dddd, d MMMM yyyy HH:mm:ss");
            }
            if (uniqueTime == m_currentReading.pubDate) {
                 m_subscription->badDates= true;
            }
            if (m_currentReading.pubDate.isNull()) {
                qDebug() << "pubdate: " << dateStr << m_currentReading.pubDate.toString();
            }
            uniqueTime = m_currentReading.pubDate;
        }
        QXmlStreamAttributes attrs = m_streamReader.attributes();
        if ((name == "guid") || (name == "link")) {
            if (attrs.hasAttribute("isPermaLink")) {
                    if (attrs.value("isPermaLink") == "false") continue;
            }
            QString str = m_streamReader.readElementText().trimmed();
            if (!str.isEmpty())
                m_currentReading.downloadLink = QUrl::fromUserInput(str);
        }
        else if (((name == "content") || (name == "origLink") || (name == "enclosure"))
             && attrs.hasAttribute("url")) {
            QString str = attrs.value("url").toString().trimmed();
            QUrl u = QUrl::fromUserInput(str);
//            qDebug() << "url=" << u.toString();
            m_currentReading.downloadLink = u;
        }
    }
    // release resources
    m_streamReader.clear();
    Completers::add(m_subscription->name, m_subscription->url.toString());
    emit endResetModel();
    emit subscriptionUpdated(m_subscription);
}




//------------------------- List of subscriptions --------------------

SubscriptionListModel::SubscriptionListModel(QObject *parent) :
    QAbstractListModel(parent) {
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[UrlRole] = "url";
    roles[LastCheckedRole] = "lastChecked";
    setRoleNames(roles);
    restore();
}

void SubscriptionListModel::subscribe(QString name, QUrl url) {
    Subscription s(name, url);
    emit beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_subscriptions << s;
    emit endInsertRows();
    save();
}

bool SubscriptionListModel::removeRows(int row, int count, const QModelIndex &) {
    emit beginRemoveRows(QModelIndex(), row, row+count-1);
    for (int i=0; i<count; ++i) {
        m_subscriptions.removeAt(row);
    }
    emit endRemoveRows();
    save();
    return true;
}

void SubscriptionListModel::restore() {
    QSettings s;
    if (!s.contains("podcastdir")) {
        QString songsDir = QDir::currentPath();
        if (s.contains("songsdir")) {
            songsDir = s.value("songsdir").toString();
        }
        QString newDir = QFileDialog::getExistingDirectory(0, tr("Choose Podcast Folder"), songsDir);
        if (newDir.isEmpty()) return;
        s.setValue("podcastdir", newDir);
        if (s.value("songsdir", "/").toString() == "/")
            s.setValue("songsdir", newDir);
    }
    QString path = subscriptionsFile();
    QFile f(path);
    if (f.open(QIODevice::ReadOnly)) {
        QTextStream ts(&f);
        QString line = ts.readLine();
        while (!line.isEmpty()) {
            // ignore comments
            if (!line.startsWith("#")) {
                // tab-separated values.
                QStringList fields = line.split("\t");
                if (fields.count() == 2) {
                    Subscription sub;
                    sub.url = fields[0];
                    sub.name = fields[1];
                    m_subscriptions << sub;
                }
            }
            line = ts.readLine();
        }
    }
    else {
        Subscription sub("No subscriptions", QUrl("subscribed.tsv"));
        m_subscriptions << sub;
    }
}

void SubscriptionListModel::save() {
    QString path = subscriptionsFile();
    QFile f(path);
    if (f.open(QIODevice::WriteOnly)) {
        QTextStream ts(&f);
        foreach (Subscription s, m_subscriptions) {
              ts << s.url.toEncoded() << "\t" << s.name << endl;
        }
        f.close();
    }
}

QString SubscriptionListModel::subscriptionsFile() const {
    QSettings s;
    QString podcastDir = s.value("podcastdir").toString();
    QDir dir(podcastDir);
    QString path = dir.absoluteFilePath("subscribed.tsv");
    QStringList entries = dir.entryList(QStringList() << "*.tsv");
    if (!entries.isEmpty()) {
        path = dir.absoluteFilePath(entries[0]);
        qDebug() << "Found subscriptions file: " << path;
    }
    return path;
}

QVariant SubscriptionListModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid()) return QVariant();
    int r = index.row();
    if (r < 0) return QVariant();
    if (r >= m_subscriptions.count()) return QVariant();
    if ((role == Qt::DisplayRole) || (role == NameRole)) {
        QDateTime lastChecked = m_subscriptions[r].lastChecked();
        if (lastChecked.isNull())
            return m_subscriptions[r].name;
        else return m_subscriptions[r].name + " (updated: "
                + ago(lastChecked) + ")";
    }
    else if ((role == Qt::ToolTipRole) || (role == UrlRole) || (role == Qt::EditRole)) {
        return m_subscriptions[r].url.toString();
    }
    else if (role == LastCheckedRole) {
        QDateTime lastChecked = m_subscriptions[r].lastChecked();
        if (lastChecked.isNull()) return QString("");
        return lastChecked.toString("yy-MMM-dd hh:mm:ss");
    }
    return QVariant();
}


int SubscriptionListModel::rowCount(const QModelIndex & parent) const {
    if (parent == QModelIndex())
    return m_subscriptions.count();
    else return 0;
}

Subscription &SubscriptionListModel::subscription(int row) {
    return m_subscriptions[row];
}

void SubscriptionListModel::reload(int row) {
    if (row > -1) {
        QString p = m_subscriptions[row].downloadPath("episodes.xml");
        QFile f(p);
        f.remove();
        emit subscriptionUpdated(&m_subscriptions[row]);
        emit dataChanged(index(row,0), index(row, 0));
        return;
    }
    for (int i=0; i<rowCount(); ++i) {
        QString p = m_subscriptions[i].downloadPath("episodes.xml");
        QFile f(p);
        f.remove();
        emit subscriptionUpdated(&m_subscriptions[i]);
    }
    emit dataChanged(index(0,0), index(rowCount(), 0));
}

void SubscriptionListModel::updateSubscription(Subscription *s) {
    for (int i=0; i<m_subscriptions.count(); ++i) {
        if (s == &m_subscriptions[i]) {
            emit dataChanged(index(i,0), index(i,0));
            return;
        }

    }

}
